package com.mk;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreakerStrategy;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.EventObserverRegistry;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.util.TimeUtil;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author makai
 * @Description: 编码方式做资源限流
 * @Date 2023/12/6 16:49
 */
public class DemoTest {
//    static {
//        initFlowQpsRule();
//    }
    /**
     * 限流测试
     */
    @Test
    public void flowLimitTest() {
        initFlowQpsRule();
        for (int i = 0; i < 10; i++)
            // 1.5.0 版本开始可以利用 try-with-resources 特性
            // 资源名可使用任意有业务语义的字符串，比如方法名、接口名或其它可唯一标识的字符串。
            try (Entry entry = SphU.entry("resource-A")) {
                // 被保护的业务逻辑
                // do something here...
                System.out.println("resource-A业务被执行");
            } catch (BlockException ex) {
                // 资源访问阻止，被限流或被降级
                // 在此处进行相应的处理操作
                System.out.println("resource-A业务被限流了===========================");
            }
    }


    private static void initFlowQpsRule() {
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule1 = new FlowRule();
        rule1.setResource("resource-A");
        // Set max qps to 20
        rule1.setCount(2);
        rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule1.setLimitApp("default");
//        rule1.setControlBehavior(2);
        rules.add(rule1);
        FlowRuleManager.loadRules(rules);
    }

    /**
     * 熔断降级测试
     */
    @Test
    public void degradeTest() {
        initDegradeRule();
        for (int i = 0; i < 15; i++) {
            Entry entry = null;
            try {
                entry = SphU.entry("resource-A");
                if (i < 7) {
                    throw new RuntimeException();
                }
                System.out.println("业务被调用");
                // Write your biz code here.
                // <<BIZ CODE>>
            } catch (Throwable t) {
                if (!BlockException.isBlockException(t)) {
                    //必须要在异常时记录才能统计上
                    Tracer.trace(t);
                }
            } finally {
                if (entry != null) {
                    entry.exit();
                }
            }
        }
    }

    private static void initDegradeRule() {
        List<DegradeRule> rules = new ArrayList<>();
        DegradeRule rule = new DegradeRule("resource-A")
                .setGrade(CircuitBreakerStrategy.ERROR_RATIO.getType())
                //异常比例
                .setCount(0.7) // Threshold is 70% error ratio
                //达到最小调用数才开始统计
                .setMinRequestAmount(1)
//                统计窗口时长
                .setStatIntervalMs(3000) // 3s
                //熔断时长
                .setTimeWindow(1);
        rules.add(rule);
        DegradeRuleManager.loadRules(rules);
    }


    /**
     * 熔断降级监听器测试
     */
    @Test
    public void degradeTest2() {
//        熔断器规则
        initDegradeRule();
        //熔断器状态的监听器
        EventObserverRegistry.getInstance().addStateChangeObserver("logging",
                (prevState, newState, rule, snapshotValue) -> {
                    if (newState == CircuitBreaker.State.OPEN) {
                        // 变换至 OPEN state 时会携带触发时的值
                        System.err.println(String.format("%s -> OPEN at %d, snapshotValue=%.2f", prevState.name(),
                                TimeUtil.currentTimeMillis(), snapshotValue));
                    } else {
                        System.err.println(String.format("%s -> %s at %d", prevState.name(), newState.name(),
                                TimeUtil.currentTimeMillis()));
                    }
                });
//业务
        for (int i = 0; i < 15; i++) {
            Entry entry = null;
            try {
                entry = SphU.entry("resource-A");
                if (i < 7) {
                    throw new RuntimeException();
                }
                System.out.println("业务被调用");
                // Write your biz code here.
                // <<BIZ CODE>>
            } catch (Throwable t) {
                if (!BlockException.isBlockException(t)) {
                    //必须要在异常时记录才能统计上
                    Tracer.trace(t);
                }
            } finally {
                if (entry != null) {
                    entry.exit();
                }
            }
        }
    }


}
